﻿package me.zhengjie.common.utils

import cn.hutool.core.codec.Base64
import cn.hutool.core.io.IoUtil
import cn.hutool.core.util.IdUtil
import cn.hutool.poi.excel.ExcelUtil
import com.alibaba.fastjson.JSONObject
import me.zhengjie.common.exception.BadRequestException
import jakarta.servlet.ServletOutputStream
import jakarta.servlet.http.HttpServletResponse
import org.springframework.boot.system.ApplicationHome
import org.springframework.web.multipart.MultipartFile
import java.io.*
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
import java.security.MessageDigest
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.*
import javax.activation.MimetypesFileTypeMap



/**
 * File工具类，扩展 hutool 工具包
 * @author Kuki Wu
 * @date 2018-12-27
 */
object FileUtil : cn.hutool.core.io.FileUtil() {
    /**
     * 定义GB的计算常量
     */
    private const val GB = 1024 * 1024 * 1024

    /**
     * 定义MB的计算常量
     */
    private const val MB = 1024 * 1024

    /**
     * 定义KB的计算常量
     */
    private const val KB = 1024

    /**
     * 格式化小数
     */
    private val DF = DecimalFormat("0.00")

    /**
     * MultipartFile转File
     */
    @JvmStatic
    fun toFile(multipartFile: MultipartFile): File? {
        // 获取文件名
        val fileName = multipartFile.originalFilename
        // 获取文件后缀
        val prefix = "." + getExtensionName(fileName)
        var file: File? = null
        try {
            // 用uuid作为文件名，防止生成的临时文件重复
            file = File.createTempFile(IdUtil.simpleUUID(), prefix)
            // MultipartFile to File
            multipartFile.transferTo(file)
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return file
    }

    @JvmStatic
    val jarFilePath: String?
        //linux和windows下通用，获取JAR所在路径
        get() = try {
            val home = ApplicationHome(FileUtil::class.java)
            var jarFile: File? = null
            jarFile = if (home.source != null) {
                home.source
            } else {
                home.dir
            }
            jarFile.parentFile.toString()
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }

    /**
     * 获取文件扩展名，不带 .
     */
    @JvmStatic
    fun getExtensionName(filename: String?): String? {
        if (filename != null && filename.length > 0) {
            val dot = filename.lastIndexOf('.')
            if (dot > -1 && dot < filename.length - 1) {
                return filename.substring(dot + 1)
            }
        }
        return filename
    }

    /**
     * Java文件操作 获取不带扩展名的文件名
     */
    @JvmStatic
    fun getFileNameNoEx(filename: String?): String? {
        if (filename != null && filename.length > 0) {
            val dot = filename.lastIndexOf('.')
            if (dot > -1 && dot < filename.length) {
                return filename.substring(0, dot)
            }
        }
        return filename
    }

    /**
     * 文件大小转换
     */
    @JvmStatic
    fun getSize(size: Long): String {
        val resultSize: String
        resultSize = if (size / GB >= 1) {
            //如果当前Byte的值大于等于1GB
            DF.format((size / GB.toFloat()).toDouble()) + "GB   "
        } else if (size / MB >= 1) {
            //如果当前Byte的值大于等于1MB
            DF.format((size / MB.toFloat()).toDouble()) + "MB   "
        } else if (size / KB >= 1) {
            //如果当前Byte的值大于等于1KB
            DF.format((size / KB.toFloat()).toDouble()) + "KB   "
        } else {
            size.toString() + "B   "
        }
        return resultSize
    }

    /**
     * inputStream 转 File
     */
    @Throws(Exception::class)
    fun inputStreamToFile(ins: InputStream, name: String): File {
        val file = File(System.getProperty("java.io.tmpdir") + File.separator + name)
        if (file.exists()) {
            return file
        }
        val os: OutputStream = FileOutputStream(file)
        var bytesRead: Int
        val buffer = ByteArray(8192)
        while (ins.read(buffer, 0, 8192).also { bytesRead = it } != -1) {
            os.write(buffer, 0, bytesRead)
        }
        os.close()
        ins.close()
        return file
    }

    /**
     * 将文件名解析成文件的上传路径
     */
    @JvmStatic
    fun upload(file: MultipartFile, filePath: String): File? {
        val date = Date()
        val format = SimpleDateFormat("yyyyMMddhhmmssS")
        val name = getFileNameNoEx(file.originalFilename)
        val suffix = getExtensionName(file.originalFilename)
        val nowStr = "-" + format.format(date)
        try {
            val fileName = "$name$nowStr.$suffix"
            val path = filePath + fileName
            val dest = File(path)
            // 检测是否存在目录
            if (!dest.parentFile.exists()) {
                dest.parentFile.mkdirs()
            }
            // 文件写入
            file.transferTo(dest)
            return dest
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @Throws(Exception::class)
    fun fileToBase64(file: File): String {
        val inputFile = FileInputStream(file)
        val base64: String
        val buffer = ByteArray(file.length().toInt())
        inputFile.read(buffer)
        inputFile.close()
        base64 = Base64.encode(buffer)
        return base64.replace("[\\s*\t\n\r]".toRegex(), "")
    }

    @JvmStatic
    /**
     * 导出excel
     */
    @Throws(IOException::class)
    fun downloadExcel(list: List<Map<String?, Any?>?>?, response: HttpServletResponse) {
        val tempPath = System.getProperty("java.io.tmpdir") + IdUtil.fastSimpleUUID() + ".xlsx"
        val file = File(tempPath)
        val writer = ExcelUtil.getBigWriter(file)
        // 一次性写出内容，使用默认样式，强制输出标题
        writer.write(list, true)
        //response为HttpServletResponse对象
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8")
        //test.xls是弹出下载对话框的文件名，不能为中文，中文请自行编码
        response.setHeader("Content-Disposition", "attachment;filename=file.xlsx")
        val out: ServletOutputStream = response.getOutputStream()
        // 终止后删除临时文件
        file.deleteOnExit()
        writer.flush(out, true)
        //此处记得关闭输出Servlet流
        IoUtil.close(out)
    }

    @JvmStatic
    fun getFileType(type: String?): String {
        val documents = "txt doc pdf ppt pps xlsx xls docx"
        val music = "mp3 wav wma mpa ram ra aac aif m4a"
        val video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg"
        val image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg"
        return if (image.contains(type!!)) {
            "图片"
        } else if (documents.contains(type)) {
            "文档"
        } else if (music.contains(type)) {
            "音乐"
        } else if (video.contains(type)) {
            "视频"
        } else {
            "其他"
        }
    }

    @JvmStatic
    fun getFileTypeByMimeType(type: String): String {
        val mimeType = MimetypesFileTypeMap().getContentType(".$type")
        return mimeType.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
    }

    @JvmStatic
    fun checkSize(maxSize: Long, size: Long) {
        if (size > maxSize * 1024 * 1024) {
            throw BadRequestException("文件超出规定大小")
        }
    }

    @JvmStatic
    /**
     * 判断两个文件是否相同
     */
    fun check(file1: File, file2: File): Boolean {
        val img1Md5 = getMd5(file1)
        val img2Md5 = getMd5(file2)
        return img1Md5 == img2Md5
    }

    @JvmStatic
    /**
     * 判断两个文件是否相同
     */
    fun check(file1Md5: String, file2Md5: String): Boolean {
        return file1Md5 == file2Md5
    }

    @JvmStatic
    private fun getByte(file: File): ByteArray? {
        // 得到文件长度
        val b = ByteArray(file.length().toInt())
        try {
            val `in`: InputStream = FileInputStream(file)
            try {
                `in`.read(b)
            } catch (e: IOException) {
                e.printStackTrace()
            }
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
            return null
        }
        return b
    }

    @JvmStatic
    private fun getMd5(bytes: ByteArray?): String? {
        // 16进制字符
        val hexDigits = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f')
        try {
            val mdTemp = MessageDigest.getInstance("MD5")
            mdTemp.update(bytes)
            val md = mdTemp.digest()
            val j = md.size
            val str = CharArray(j * 2)
            var k = 0
            // 移位 输出字符串
            for (byte0 in md) {
                str[k++] = hexDigits[byte0.toInt() ushr 4 and 0xf]
                str[k++] = hexDigits[byte0.toInt() and 0xf]
            }
            return String(str)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    @JvmStatic
    fun getMd5(file: File): String? {
        return getMd5(getByte(file))
    }

    @JvmStatic
    /**
     * 创建目录
     * @param destDirName
     * 目标目录名
     * @return 目录创建成功返回true，否则返回false
     */
    fun createDir(destDirName: String): Boolean {
        var destDirName = destDirName
        val dir = File(destDirName)
        if (dir.exists()) {
            return false
        }
        if (!destDirName.endsWith(File.separator)) {
            destDirName = destDirName + File.separator
        }
        // 创建单个目录
        return dir.mkdirs()
    }

    /**
     * 删除文件
     * @param filePathAndName
     * String 文件路径及名称 如c:/fqf.txt
     */
    @JvmStatic
    fun delFile(filePathAndName: String) {
        try {
            //filePath = filePath.toString();
            val myDelFile = File(filePathAndName)
            myDelFile.delete()
        } catch (e: Exception) {
            println("删除文件操作出错")
            e.printStackTrace()
        }
    }

    /**
     * 写入文件
     * @param filePath   路径
     * @param strContent 文本内容
     */
    @JvmStatic
    fun writeTxtFile(filePath: String?, strContent: String) {
        // TODO Auto-generated method stub
        // file(内存)----输入流---->【程序】----输出流---->file(内存)
        val file = File(filePath)
        try {
            file.createNewFile() // 创建文件
        } catch (e: IOException) {
            // TODO Auto-generated catch block
            e.printStackTrace()
        }

        // 向文件写入内容(输出流)
        var bt = ByteArray(1024)
        bt = strContent.toByteArray()
        try {
            val `in` = FileOutputStream(file)
            try {
                `in`.write(bt, 0, bt.size)
                `in`.close()
                // boolean success=true;
                // System.out.println("写入文件成功");
            } catch (e: IOException) {
                // TODO Auto-generated catch block
                e.printStackTrace()
            }
        } catch (e: FileNotFoundException) {
            // TODO Auto-generated catch block
            e.printStackTrace()
        }
    }

    /**
     * 读取到字节数组0
     * @param filePath //路径
     * @return  byte[]
     * @throws IOException
     */
    @JvmStatic
    @Throws(IOException::class)
    fun getContent(filePath: String?): ByteArray? {
        val file = File(filePath)
        val fileSize = file.length()
        if (fileSize > Int.MAX_VALUE) {
            println("file too big...")
            return null
        }
        val fi = FileInputStream(file)
        val buffer = ByteArray(fileSize.toInt())
        var offset = 0
        var numRead = 0
        while (buffer.size >= offset
            && fi.read(buffer, offset, buffer.size - offset).also { numRead = it } >= 0
        ) {
            offset += numRead
        }
        // 确保所有数据均被读取
        if (offset != buffer.size) {
            throw IOException("Could not completely read file " + file.name)
        }
        fi.close()
        return buffer
    }

    /**
     * 读取到字节数组1
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    @JvmStatic
    @Throws(IOException::class)
    fun toByteArray(filePath: String?): ByteArray {
        val f = File(filePath)
        if (!f.exists()) {
            throw FileNotFoundException(filePath)
        }
        val bos = ByteArrayOutputStream(f.length().toInt())
        var `in`: BufferedInputStream? = null
        return try {
            `in` = BufferedInputStream(FileInputStream(f))
            val buf_size = 1024
            val buffer = ByteArray(buf_size)
            var len = 0
            while (-1 != `in`.read(buffer, 0, buf_size).also { len = it }) {
                bos.write(buffer, 0, len)
            }
            bos.toByteArray()
        } catch (e: IOException) {
            e.printStackTrace()
            throw e
        } finally {
            try {
                `in`!!.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
            bos.close()
        }
    }

    /**
     * 读取到字节数组2
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    @JvmStatic
    @Throws(IOException::class)
    fun toByteArray2(filePath: String?): ByteArray {
        val f = File(filePath)
        if (!f.exists()) {
            throw FileNotFoundException(filePath)
        }
        var channel: FileChannel? = null
        var fs: FileInputStream? = null
        return try {
            fs = FileInputStream(f)
            channel = fs.channel
            val byteBuffer = ByteBuffer.allocate(channel.size().toInt())
            while (channel.read(byteBuffer) > 0) {
                // do nothing
                // System.out.println("reading");
            }
            byteBuffer.array()
        } catch (e: IOException) {
            e.printStackTrace()
            throw e
        } finally {
            try {
                channel!!.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
            try {
                fs!!.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }

    /**
     * Mapped File way MappedByteBuffer 可以在处理大文件时，提升性能
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    @JvmStatic
    @Throws(IOException::class)
    fun toByteArray3(filePath: String?): ByteArray {
        var fc: FileChannel? = null
        var rf: RandomAccessFile? = null
        return try {
            rf = RandomAccessFile(filePath, "r")
            fc = rf.channel
            val byteBuffer = fc.map(
                FileChannel.MapMode.READ_ONLY, 0,
                fc.size()
            ).load()
            //System.out.println(byteBuffer.isLoaded());
            val result = ByteArray(fc.size().toInt())
            if (byteBuffer.remaining() > 0) {
                // System.out.println("remain");
                byteBuffer[result, 0, byteBuffer.remaining()]
            }
            result
        } catch (e: IOException) {
            e.printStackTrace()
            throw e
        } finally {
            try {
                rf!!.close()
                fc!!.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }

    /**
     * 读取普通文本文件到字符串类型
     * @param fileP
     * @return String
     */
    @JvmStatic
    fun toString(fileP: String?): String? {
        var txt: String? = ""
        try {
            val encoding = "utf-8"
            /*			String filePath = String.valueOf(Thread.currentThread().getContextClassLoader().getResource("")); // 项目路径
                        filePath=new File(filePath).getParent();
                        //String filePath = String.valueOf(Thread.currentThread().getContextClassLoader().getResource("")); // 项目路径
			filePath = filePath.replaceAll("file:/", "");
			filePath = filePath.replaceAll("%20", " ");
			filePath = filePath.trim() + fileP.trim();
			if (filePath.indexOf(":") != 1) {
				filePath = File.separator + filePath;
			}*/
            val file = File(fileP)
            if (file.isFile && file.exists()) { // 判断文件是否存在
                val read = InputStreamReader(
                    FileInputStream(file), encoding
                ) // 考虑到编码格式
                val bufferedReader = BufferedReader(read)
                var lineTxt: String? = null
                while (bufferedReader.readLine().also { lineTxt = it } != null) {
                    //System.out.println(lineTxt);
                    txt += lineTxt
                }
                read.close()
            } else {
                println("找不到指定的文件")
            }
        } catch (e: IOException) {
            println("读取文件内容出错")
            e.printStackTrace()
        }
        return txt
    }

    /**
     * 读取普通文本文件到JSON对象
     * @param filePath
     * @return String
     */
    @JvmStatic
    fun toJSON(filePath: String?): JSONObject? {
        return try {
            val content = toString(filePath)
            JSONObject.parseObject(content)
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    @JvmStatic
    @Throws(IOException::class)
    fun FileToBase64(filePath: String): String? {
        var strBase64: String? = null
        try {
            val `in`: InputStream = FileInputStream(filePath)
            // in.available()返回文件的字节长度
            val bytes = ByteArray(`in`.available())
            // 将文件中的内容读入到数组中
            `in`.read(bytes)
            strBase64 = Base64.encode(bytes) //将字节流数组转换为字符串
            `in`.close()
        } catch (fe: FileNotFoundException) {
            fe.printStackTrace()
        } catch (ioe: IOException) {
            ioe.printStackTrace()
        }
        return strBase64
    }

    @JvmStatic
    @Throws(IOException::class)
    fun base64ToFile(strBase64: String?, toFilePath: String) {
        try {
            // 解码，然后将字节转换为文件
            val bytes = Base64.decode(strBase64!!.toByteArray()) //将字符串转换为byte数组
            val `in` = ByteArrayInputStream(bytes)
            val buffer = ByteArray(1024)
            val out = FileOutputStream(toFilePath)
            var bytesum = 0
            var byteread = 0
            while (`in`.read(buffer).also { byteread = it } != -1) {
                bytesum += byteread
                out.write(buffer, 0, byteread) //文件写操作
            }
            out.close()
        } catch (ioe: IOException) {
            ioe.printStackTrace()
        }
    }

    @JvmStatic
    fun byteToHexString(bArray: ByteArray): String {
        val offset = 0
        val len = bArray.size
        val sb = StringBuffer(len)
        var sTemp: String
        for (i in offset until offset + len) {
            sTemp = Integer.toHexString(0xFF and bArray[i].toInt())
            if (sTemp.length < 2) sb.append(0)
            sb.append(sTemp.uppercase(Locale.getDefault()))
        }
        return sb.toString()
    }

    @JvmStatic
    fun hexToByteArray(hexString: String): ByteArray {
        val len = hexString.length
        val data = ByteArray(len / 2)
        var i = 0
        while (i < len) {
            data[i / 2] = ((hexString[i].digitToIntOrNull(16) ?: -1 shl 4) + hexString[i + 1].digitToIntOrNull(16)!!
                ?: -1).toByte()
            i += 2
        }
        return data
    }

    /**
     * 获得指定文件的byte数组
     * @param filePath 文件绝对路径
     * @return
     */
    @JvmStatic
    fun file2Byte(filePath: String?): ByteArray? {
        var bos: ByteArrayOutputStream? = null
        var `in`: BufferedInputStream? = null
        return try {
            val file = File(filePath)
            if (!file.exists()) {
                throw FileNotFoundException("file not exists")
            }
            bos = ByteArrayOutputStream(file.length().toInt())
            `in` = BufferedInputStream(FileInputStream(file))
            val buf_size = 1024
            val buffer = ByteArray(buf_size)
            var len = 0
            while (-1 != `in`.read(buffer, 0, buf_size).also { len = it }) {
                bos.write(buffer, 0, len)
            }
            bos.toByteArray()
        } catch (e: Exception) {
            println(e.message)
            e.printStackTrace()
            null
        } finally {
            try {
                `in`?.close()
                bos?.close()
            } catch (e: Exception) {
                println(e.message)
                e.printStackTrace()
            }
        }
    }

    /**
     * 根据byte数组，生成文件
     * @param bfile 文件数组
     * @param filePath 文件存放路径
     * @param fileName 文件名称
     */
    @JvmStatic
    fun byte2File(bfile: ByteArray?, filePath: String, fileName: String) {
        var bos: BufferedOutputStream? = null
        var fos: FileOutputStream? = null
        var file: File? = null
        try {
            val dir = File(filePath)
            if (!dir.exists() && !dir.isDirectory) { //判断文件目录是否存在
                dir.mkdirs()
            }
            file = File(filePath + fileName)
            fos = FileOutputStream(file)
            bos = BufferedOutputStream(fos)
            bos.write(bfile)
        } catch (e: Exception) {
            println(e.message)
            e.printStackTrace()
        } finally {
            try {
                bos?.close()
                fos?.close()
            } catch (e: Exception) {
                println(e.message)
                e.printStackTrace()
            }
        }
    }

    /*@JvmStatic
    fun main(args: Array<String>) {
        //String dirName = "d:/FH/topic/";// 创建目录
        //FileUtil.createDir(dirName);
        /*String fileName = Const.APPDVERSIONFILE;
                System.out.print(FileUtil.toString(fileName).toString());
                JSONObject json=FileUtil.toJSON(fileName);
                if(json!=null){
                    System.out.println("");
                    System.out.println("versionCode:"+json.getString("versionCode"));
                    System.out.println("downUrl:"+json.getString("downUrl"));
                }else{
                    System.out.println("Nothing");
                }*/
        //Logger.getLogger(FileUtil.class.getName()).log(Level.ALL,FileUtil.toString(fileName));
        var b64: String? = ""
        try {
            println(jarFilePath)
            b64 = FileToBase64("D:/373.PTE")
        } catch (e: IOException) {
            e.printStackTrace()
        }
        try {
            base64ToFile(b64, "D:/373_R.PTE")
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }*/
}
